#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include "hash.h"

#define HASH_BUCKET_ENTRIES 4

struct hash *hash_create(int cap, hdigest_t digest, hcmp_t cmp, hindex_t index)
{
  int bucket_num = (cap + HASH_BUCKET_ENTRIES - 1) / HASH_BUCKET_ENTRIES;
  if (bucket_num < HASH_BUCKET_ENTRIES)
    bucket_num = HASH_BUCKET_ENTRIES;
  int total = HASH_BUCKET_ENTRIES * bucket_num;
  int size = sizeof(struct hash);
  size += bucket_num * sizeof(struct hash_entry *);
  size += total * sizeof(struct hash_entry);
  struct hash *h = malloc(size);
  if (!h)
    return NULL;
  h->bucket_num = bucket_num;
  h->digest = digest;
  h->cmp = cmp;
  h->index = index;
  h->pool = NULL;
  int i;
  for (i = 0; i < h->bucket_num; i++)
    h->buckets[i] = NULL;
  struct hash_entry *z = 
     (struct hash_entry *)((uint8_t*)(h+1) + h->bucket_num * sizeof(struct hash_entry *));
  for (i = 0; i < total; i++) {
    (&z[i])->next = h->pool;
    h->pool = (&z[i]);
  }
  return h;
}

static struct hash_entry *_hash_get(struct hash *hash, void *key)
{
  int digest = hash->digest(key);
  int buckets = digest % hash->bucket_num;
  struct hash_entry * lookup = hash->buckets[buckets];
  while(lookup) {
    if (!hash->cmp(key, hash->index(lookup->index)))
      break;
    lookup=lookup->next;
  }
  return lookup;
}

int hash_put(struct hash *hash, int index)
{
  void *key = hash->index(index);
  struct hash_entry *e = _hash_get(hash, key);
  if (!e) {
    e = hash->pool;
    if (e) {
      hash->pool = e->next;
      e->index = index;
      int buckets = hash->digest(key) % hash->bucket_num;
      e->next = hash->buckets[buckets];
      hash->buckets[buckets] = e;
    }
  }
  return e != NULL ? 0 : -1;
}

int hash_get(struct hash *hash, void *key)
{
  struct hash_entry *e = _hash_get(hash, key);
  return e != NULL ? e->index : -1;
}

void hash_destroy(struct hash *hash)
{
  if(hash)
    free(hash);
}
